/** \file    Segmentation.h
  * \brief   Segmentation of an image into pixelwise regions that stores among others 
  *			 connectivity of regions.
  * \date    2001 - 2003
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/include/segm/Segmentation.h,v 1.2 2005/04/04 04:10:16 cscom1r4 Exp $
  */

#ifndef _Segmentation_h_
#define _Segmentation_h_

#include <string>
#include "imglib/imageclass_decl.h"
#include "xtd/indexed_vector.h"

#include "Segment.h"
#include "Vec2iTree.h"
#include "ProfileParams.h"
#include "MotionParams.h"

namespace types { 
	template <class T> struct basic_affine;
	typedef basic_affine<double> affine;
}
using namespace types;

typedef std::vector<double_point> DoublePointArr;
typedef std::vector<double> DoubleArr;
typedef std::vector<bool>	BoolArr;
typedef std::vector<int>	IntArr;

///Simplest Segmentation that finds only connectivity of regions
class Segmentation {
public:
	typedef xtd::indexed_vector<Segment>	segmentIvector; ///< index - color of segment in segmentation image
	typedef segmentIvector::iterator		iterator;
	typedef segmentIvector::const_iterator	const_iterator;

public:
	//////////////////
	// Construction //
	//////////////////

	Segmentation();
	void clear();
	void create(const segm_image & source);
	///before creation of the object, purifies s with theguaranty 
	///that no color appears in two unconnected segments simultaneously
	void create(segm_image & s, const byte_image & i);
	///creates segmentation, dividing masked region on squares of given size.
	void squareCreate(const img::bitmap & mask, 
		size_t size, size_t offset = 0, const byte_image * colorateImg = 0);

	////////////////
	// Attributes //
	////////////////

	///the number of segments
	int count() const 
		{ return m_segmentIvector.size(); }

	iterator segmentsBegin() 
		{ return m_segmentIvector.begin(); }
	const_iterator segmentsBegin() const 
		{ return m_segmentIvector.begin(); }
	iterator segmentsEnd() 
		{ return m_segmentIvector.end(); }
	const_iterator segmentsEnd() const 
		{ return m_segmentIvector.end(); }

	/** \name Segment Accessors */
	//@{
	///fastest function - no comparison inside, return -1 on no segment
	int segmentIndex(int x, int y) const;
	int segmentIndex(const size_point &p) const
		{ return segmentIndex(p.x, p.y); }
	int segmentIndex(int color) const
		{ return m_segmentIvector.find_seqnum(color); }
	int segmentIndex(const byte_color & c) const
		{ return segmentIndex(color2int(c)); }

	//safe pointer returns - 0 if there is no requested segment
	Segment *pSegmentByColor( int color )
		{ return m_segmentIvector.find_by_index(color); }
	Segment *pSegmentByColor(const byte_color & c)
		{ return pSegmentByColor(color2int(c)); }
	const Segment *pSegmentByColor( int color ) const
		{ return m_segmentIvector.find_by_index(color); }
	const Segment *pSegmentByColor(const byte_color & c) const
		{ return pSegmentByColor(color2int(c)); }
	Segment *pSegmentByIndex( int index )
		{ return &m_segmentIvector[index]; }
	const Segment *pSegmentByIndex( int index ) const
		{ return &m_segmentIvector[index]; }
	Segment *pSegment( int x, int y );
	const Segment *pSegment( int x, int y ) const;

	//assertion inside
	Segment &segmentByColor( int color )
		{ return m_segmentIvector(color); }
	const Segment &segmentByColor( int color ) const
		{ return m_segmentIvector(color); }
	Segment &segmentByIndex( int index )
		{ return m_segmentIvector[index]; }
	const Segment &segmentByIndex( int index ) const
		{ return m_segmentIvector[index]; }
	Segment &segment( int x, int y );
	const Segment &segment( int x, int y ) const;

	const YATL::Arr2i& segmentCoordMap() const 
		{ return m_segmentCoordMap; }
	//@}

	//dimensions of image
	int width() const 
		{ return m_width; }
	int height() const 
		{ return m_height; }

	//original image that was segmented
	const float_image *sourceImage() const
		{ return m_sourceImage; }
	void setSourceImage(const float_image *s)
		{ m_sourceImage = s; 
		  m_colorStatisticsFound = false; }

	//segm. image location
	const std::string &segmImageFile() const
		{ return m_segmImageFile; }
	void setSegmImageFile(const std::string &newVal)
		{ m_segmImageFile = newVal; }
	void setSegmImageFile(const std::string & dir, const std::string & name)
		{ m_segmImageFile = dir + '/' + name; }

	////////////////
	// Operations //
	////////////////

	///the member function swaps the controlled sequences between *this and str
	void swap(Segmentation & str);

	///determines boundingBox and center for each segment
	void positions() const;
	///produces mask of segment having size of segment's bounding box
	///if ext is zero or large in each direction if ext is above zero
	void segmentMask(int sg, img::bitmap & mask, unsigned ext = 0) const;
	///produces tree (for fast search) with mapping: segment centre to its index
	void centresTree(VecIndTree & t);

	///removes inactive segments from segmentation.
	void reduce();
	///elements in C corresponds to current segments, grouped according xtd::group, with
	///correct values of 'pixels' member in group roots, groups wll correspond to new segments
	template <class C>
	void merge(C & c);

	///in place merges too small segments with nearest neighbours
	void mergeSmall( int smallPcount, int targetPcount );
	///merge segments to reduce dispersion of color in them and eliminate small segments
	void mergeByColor(size_t smallPcount, bool merge_large);
	///splits segments large then a threshold and writes result in a segmentation-picture.
	void splitLarge(int largePcount, int targetPcount, byte_image & dest,
		int splitReclusteringCycles) const;

	///(de)activates all the segments
	void activate();
	void deactivate();
	///some mask is specified by active segments; this method activetes also holes in the mask
	void activateHoles();
	///finds ratio of covering of each segment by bitmap
	void covering(const img::bitmap &bmp, DoubleArr & cover) const;
	///activates (sets flag) segments covered more than by given ratio by a given bitmap
	void activateCoveredSegments(const img::bitmap & bmp, double coveringRatio);
	///deactivates (resets flag) segments covered less than by given ratio by a given bitmap
	void deactivateCoveredSegments(const img::bitmap & bmp, double coveringRatio);
	///returns bitmap of pixels of active segments
	void activePixelsBitmap(img::bitmap & bmp) const;

	///calculates covering and corrects mask at some places
	void mixedGranulateMask(img::bitmap & bmp, double coveringRatio) const;
	///granulate mask in every segment
	void roughGranulateMask(img::bitmap & bmp) const
		{ mixedGranulateMask(bmp, 0.5); }

	///produces "foreign" segment for each border segment of mask
	void makeForeignSegments(const img::bitmap & frgnArea);
	///returns to the state before call to makeForeignSegments()
	void removeForeignSegments();

	///mean segment size in pixels
	double meanSegmentPixelCount() const;
	///sum of profile values at established displacements
	double motionQuality() const;

	///changes size of every (active) segment's profile and fills it with zeros
	void resizeProfiles(unsigned size, bool activeOnly = false);
	void eraseProfiles()
		{ resizeProfiles(0); }
	///fills profiles with correlation values
	/** awaits on entry to find predicted movement of segments 
		in 'pdisplacement' member of each segment */
	void buildProfiles(const float_image & tgt, const ProfileParams & param,
					   bool activeOnly = false);
	void buildCorrelationProfileWithTest(const float_image &tgt, 
		unsigned size, bool activeOnly = false);

	///sets mean and dispersion of colors for all segments
	void colorStatistics(bool upThrsInsteadDisp = false, float factorSqSigma = 0);

	///sets zero predicted displacements for all segments
	void noPredictedMotion();
	///checks that neither displacement during profile building moves any point of (masked) segmentation outside the frame (if check is failed - throw)
	void checkMotionInterior(size_t maxRdispl);

	///finds coherent motion of all segments and returns quadratic approximation of the motion
	template <class T>
	void coherentMotion(T & transform, const MotionParams & param);
	///buildes quadratic (general case) approximation of segments motion
	template <class T>
	void determineTransform( 
		T & t, TransformClass transformClass = TRANSFORM_AFFINE) const;
	void determineTransform2(affine & a, TransformClass transformClass = TRANSFORM_AFFINE) const;
	///displaces segments according to given transformation
	template <class T>
	void displaceByTransform(const T & t);
	///sets predicted displacement of segments according to given transformation
	template <class T>
	void predictByTransform(const T & t);

	///\name segments debug information
	//@{
	///stores (if debugFile does not empty) and then clears text associated with each segment in the log
	void logSegmDebug(const std::string & debugFile = std::string());
	///associates position and color statistics textual information with each segment
	void posSegmDebug();
	///writes plot file with data of profiles, each profile as time step
	void plotProfiles(const char *plotFileName);
	//@}

	/////////////
	// Imagine //
	/////////////

	//creates image with representation of:

	void imagineSegmentation(byte_image & img) const;
	void imagineForeignSegments(byte_image & img) const;
	void imagineGivenSegments(byte_image & img, const BoolArr & show) const;
	void imagineDisplacedSegmentation(byte_image & img) const;
	void imagineGivenDisplacedSegments(byte_image & img, const BoolArr & show) const;

private:
	///returns index of existing segment or just created segment; m_segmentCoordMap remembers else one point of that segment
	size_t newSegmentByColor( int color, int x, int y );

private:
	segmentIvector	m_segmentIvector;
	YATL::Arr2i		m_segmentCoordMap;	///< contains index of segment by coordinate
	unsigned		m_width, m_height;

	const float_image *m_sourceImage;

	std::string m_segmImageFile;

	//saves what is already found for each segment
	mutable bool m_positionsFound;
	bool		m_colorStatisticsFound;
	bool		m_upThrsInsteadDisp;
	///indicates that nothing is found due to segmentation change
	void changed();

	///\name segments debug information
	//@{
	std::string	m_plotFileName;
	//@}
};

///maintain foreign segments during object existence
class ForeignSegments
{
public:
	ForeignSegments(Segmentation & s) : _s(s), _made(false) {}
	~ForeignSegments();

	void make(const img::bitmap & foreignArea);
	void make(const img::bitmap & mask, unsigned foreignAreaWidth);

private:
	Segmentation & _s;
	bool _made;
};


/////////////
// inlines //
/////////////

///\return -1 on error
inline int Segmentation::segmentIndex( int x, int y ) const {
	return m_segmentCoordMap(x,y);
}

///\return 0 if no segment is associated with a point
inline Segment *Segmentation::pSegment( int x, int y ) {
	int i = m_segmentCoordMap(x,y);
	return (i == -1) ? 0 : &m_segmentIvector[i];
}

inline const Segment *Segmentation::pSegment( int x, int y ) const {
	return const_cast<Segmentation*>(this)->pSegment( x, y );
}

// return reference on segment

///\return 0 if no segment is associated with a point
inline Segment &Segmentation::segment( int x, int y ) {
	int i = m_segmentCoordMap(x,y);
	assert( i != -1 );
	return m_segmentIvector[i];
}

inline const Segment &Segmentation::segment( int x, int y ) const {
	return const_cast<Segmentation*>(this)->segment( x, y );
}

inline size_t Segmentation::newSegmentByColor( int color, int x, int y ) {
	return m_segmentCoordMap(x,y) = m_segmentIvector.insert(Segment(color));
}

#endif //_Segmentation_h_
